export enum TimeConst {
  Seconds = 1000,
  Minutes = 60000,
  Hours = 3600000,
  Day = 86400000,
}

// eslint-disable-next-line @typescript-eslint/prefer-namespace-keyword, @typescript-eslint/no-namespace
export namespace DateUtil {
  /** 日期格式化 用中文拼音首字母标识 */
  const DatePatterns: { [key: string]: (date: Date) => string } = {
    '{nnnn}': (date) => date.getFullYear().toString(),
    '{nn}': (date) => date.getFullYear().toString().slice(-2),
    '{y}': (date) => (date.getMonth() + 1).toString(),
    '{yy}': (date) => (date.getMonth() + 1).toString().padStart(2, '0'),
    '{r}': (date) => date.getDate().toString(),
    '{rr}': (date) => date.getDate().toString().padStart(2, '0'),
    '{s}': (date) => date.getHours().toString(),
    '{ss}': (date) => date.getHours().toString().padStart(2, '0'),
    '{f}': (date) => date.getMinutes().toString(),
    '{ff}': (date) => date.getMinutes().toString().padStart(2, '0'),
    '{m}': (date) => date.getSeconds().toString(),
    '{mm}': (date) => date.getSeconds().toString().padStart(2, '0'),
  };

  export function getNextDate(currentDate: Date, type: 'day' | 'week' | 'month'): Date {
    const date = new Date(currentDate);
    switch (type) {
      case 'day':
        date.setDate(date.getDate() + 1);
        break;
      case 'week':
        date.setDate(date.getDate() + 7 - date.getDay() + 1);
        break;
      case 'month':
        date.setMonth(date.getMonth() + 1, 1);
        break;
      default:
        throw new Error('Invalid type');
    }
    date.setHours(0, 0, 0, 0);
    return date;
  }

  export function getTimeStrToDate(model: string = '{nn}-{yy}-{rr} {ss}:{ff}:{mm}', time: number): string {
    const date = new Date(time);
    for (const pattern in DatePatterns) {
      if (model.includes(pattern)) {
        const regex = new RegExp(pattern, 'g');
        model = model.replace(regex, DatePatterns[pattern](date));
      }
    }

    return model;
  }

  /** 时间格式化 用英文首字母标识 */
  const TimePatterns: { [key: string]: (time: number) => [number, string] } = {
    '{d}': (time) => formatTimeUnit(time, TimeConst.Day, false),
    '{dd}': (time) => formatTimeUnit(time, TimeConst.Day, true),
    '{h}': (time) => formatTimeUnit(time, TimeConst.Hours, false),
    '{hh}': (time) => formatTimeUnit(time, TimeConst.Hours, true),
    '{m}': (time) => formatTimeUnit(time, TimeConst.Minutes, false),
    '{mm}': (time) => formatTimeUnit(time, TimeConst.Minutes, true),
    '{s}': (time) => formatTimeUnit(time, TimeConst.Seconds, false),
    '{ss}': (time) => formatTimeUnit(time, TimeConst.Seconds, true),
  };

  function formatTimeUnit(time: number, unit: number, padZero: boolean): [number, string] {
    const temp = Math.floor(time / unit);
    const num = temp * unit;
    const str = padZero ? temp.toString().padStart(2, '0') : temp.toString();
    return [num, str];
  }

  /** 格式化倒计时，不足的时间单位将会被忽略 */
  export function getTimeStr(model: string = '{dd}天{hh}:{mm}:{ss}', time: number): string {
    let timeStr = '';
    const separators = model.split(/\{\w*\}/g);
    const units = model.match(/\{\w*\}/g);
    for (let i = 0; i < units.length; i++) {
      const [num, str] = TimePatterns[units[i]](time);
      if (num <= 0 && timeStr == '') continue;
      timeStr += str + separators[i + 1];
      time -= num;
    }
    timeStr = separators[0] + timeStr;
    return timeStr;
  }

  /** 格式化倒计时，严格按照时间格式显示，时间为0时按格式显示，例：00:00:00 */
  export function getTimeStrFullZero(model: string = '{dd}天{hh}:{mm}:{ss}', time: number): string {
    let timeStr = '';
    const separators = model.split(/\{\w*\}/g);
    const units = model.match(/\{\w*\}/g);
    //天一般都隐藏
    for (let i = 1; i < units.length; i++) {
      const [num, str] = TimePatterns[units[i]](time);
      if (num <= 0 && timeStr == '') continue;
      timeStr += str + separators[i + 1];
      time -= num;
    }
    timeStr = separators[0] + timeStr;
    return timeStr;
  }

  /** 根据时间，获取下一个时间点
   *  @param time 参照时间
   *  @param point 时间点：从周日0点到时间点的毫秒数
   */
  export function getNextTimePointByTime(now: number = 0, point: number = 0): number {
    point = point % (TimeConst.Day * 7);
    const date = new Date(now);
    let nowDay = date.getDay();
    const dayZero = date.setHours(0, 0, 0, 0);
    let day = Math.floor(point / TimeConst.Day);
    const time = point % TimeConst.Day;
    if (day == 0) day = 7;
    if (nowDay == 0) nowDay = 7;
    if (nowDay > day) {
      return dayZero + (day - nowDay + 7) * TimeConst.Day + time;
    } else if (nowDay == day) {
      if (time >= now - dayZero) {
        return dayZero + time;
      } else {
        return dayZero + time + 7 * TimeConst.Day;
      }
    } else {
      return dayZero + (day - nowDay) * TimeConst.Day + time;
    }
  }
}
